/***************************************************************************
                          errlog.c  -  description
                        ----------------------------
    begin                : unknown
    author               : Boyan Bejanov
    email                : bbejanov@ualberta.ca
 ***************************************************************************/

/* this file uses mkstemp and fdopen which are not ansi.
    make sure your system provides these routines... */
// #ifdef __STRICT_ANSI__
// #undef __STRICT_ANSI__
// #endif

/* use this on sun, but not in gnu */

// this is for the use of mkstemp() below
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif


// these two are for the use of fileno() below
/*
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif

#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 2
#endif
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <stddef.h>
#include <stdarg.h>

#include "glodef.h"
#include "errlog.h"

// #include <petsc.h>

void PrintLogErr(const char *errtag, const char *errfmt, va_list ap);


/****************************************************************************
 *                               log                                        *
 ****************************************************************************/

static char default_log_fn[]="logfile.tmp";
static char tmpfn_template[]="logXXXXXX";
static char logfname[FILENAME_MAX];
static FILE *logF=NULL;
static int dolog=FALSE, is_log_tmp;

char *GetLogfilePath(char *buf, size_t n)
{
    if(mpi_my_id!=0) return NULL;

    if(buf)
        strncpy(buf, logfname, n);
    return buf;
}

static void InternalCloseLogfile(void)
{
    if(mpi_my_id!=0) return;

    if(logF==NULL)
        return;
    // PrintLog("Closing logfile with fileno %d.\n", fileno(logF));
    fclose(logF);
    logF=NULL;
}

void InitLogfile(const char *log_fn)
{
    // MPI_Comm_size(PETSC_COMM_WORLD, &mpi_num_procs);
    // MPI_Comm_rank(PETSC_COMM_WORLD, &mpi_my_id);

    if(mpi_my_id!=0) return;

    InternalCloseLogfile();
    if((log_fn==NULL)||(log_fn[0]==0))
    {
        int fd;
        /* Open temporary file */
        is_log_tmp=TRUE;
        strncpy(logfname, tmpfn_template, FILENAME_MAX-1);
        fd=mkstemp(logfname);
        if(fd==-1)
            error_msg("InitLogfile: cannot open temporary log file.\n");
        logF=fdopen(fd, "at");
    }
    else
    {
        is_log_tmp=FALSE;
        strncpy(logfname, log_fn, FILENAME_MAX-1);
        logF=fopen(logfname, "wt");
    }
    if(logF==NULL)
        error_msg("InitLogfile error!\nerrno=%d:%s\n",
        errno, strerror(errno));
    dolog=TRUE;
    setbuf(logF, NULL);
    {
        time_t now;
        now=time(NULL);
        PrintLog("%s", asctime(localtime(&now)));
    }
    atexit(CloseLogfile);
    PrintLog("InitLogfile successful.  ");
    PrintLog("Logging to file '%s'\n", logfname);
    // PrintLog("Logging to file '%s' with fileno %d.\n", logfname, fileno(logF));
}

void AppendLogfile(const char *log_fn)
{
    if(mpi_my_id!=0) return;

    InternalCloseLogfile();
    if((log_fn==NULL)||(log_fn[0]==0))
    {
        logF=NULL;
        dolog=FALSE;
        return;
    }
    is_log_tmp=FALSE;
    strncpy(logfname, log_fn, FILENAME_MAX-1);
    logF=fopen(logfname, "at");
    if(logF==NULL)
        error_msg("InitLogfile error!\nerrno=%d:%s\n",
        errno, strerror(errno));
    dolog=TRUE;
    setbuf(logF, NULL);
    {
        time_t now;
        now=time(NULL);
        //PrintLog("%s\tfileno is %d.\n", asctime(localtime(&now)), fileno(logF));
        PrintLog("%s", asctime(localtime(&now)));
    }
}

int RenameLogfile(const char *newlog_fn)
{
    if(mpi_my_id!=0) return FALSE;

    PrintLog("Rename logfile to '%s'", newlog_fn);
    remove(newlog_fn);
    InternalCloseLogfile();
    if((rename(logfname, newlog_fn)!=0)||((logF=fopen(newlog_fn, "r+t"))==0))
    {
        PrintLog("    failed.\n");
        warning_msg("\nerrno=%d:%s\n", errno,
            strerror(errno));
        return FALSE;
    }
    fseek(logF, 0, SEEK_END);
    strncpy(logfname, newlog_fn, FILENAME_MAX);
    is_log_tmp=FALSE;
    setbuf(logF, NULL);
    PrintLog("    success.\n");
    return TRUE;
}

int IsLogfileTemp(void)
{
    return is_log_tmp;
}

void CloseLogfile(void)
{
    if(mpi_my_id!=0) return;

    if(logF==NULL)
        return;
    if(is_log_tmp)
    {
        PrintLog("CloseLogfile: renaming to default logfile name.\n");
        RenameLogfile(default_log_fn);
    }
    //    PrintLog("\nDone.\n\n");
    fflush(logF);
    InternalCloseLogfile();
    dolog=FALSE;
}

void PrintLog(const char *logfmt, ...)
{
    if(mpi_my_id!=0) return;

    va_list ap;
    if(!dolog)
        return;
    va_start(ap, logfmt);
    vfprintf(logF, logfmt, ap);
    va_end(ap);
    return;
}

void PrintLogLine(size_t len, int c)
{
    if(mpi_my_id!=0) return;

    if(!dolog)
        return;
    for(; len>0; len--)
        fputc(c, logF);
    fputc('\n', logF);
    fflush(logF);
    return;
}

void PrintLogErr(const char *errtag, const char *errfmt, va_list ap)
{
    if(mpi_my_id!=0) return;

    /* Maybe we are loggig an error in the logging system in which case
       logF would not be available. In this case, the error message
       cannot be logged. It will still appear in sdterr */
    if(!dolog)
        return;
    if(logF==0)
        return;
    fprintf(logF, "%s", errtag);
    vfprintf(logF, errfmt, ap);
    fprintf(logF, "\n");
    return;
}

void TurnLogOn(void)
{
    dolog=TRUE;
    return;
}

void TurnLogOff(void)
{
    dolog=FALSE;
    return;
}

/****************************************************************************
 *                         error                                            *
 ****************************************************************************/

void error_msg(const char *errfmt, ...)
{
    va_list ap;
    fprintf(stderr, "\nERROR: ");
    va_start(ap, errfmt);
    vfprintf(stderr, errfmt, ap);
    va_end(ap);
    va_start(ap, errfmt);
    PrintLogErr("\nERROR: ", errfmt, ap);
    va_end(ap);
    fprintf(stderr, "\n");
    exit(EXIT_FAILURE);
}

void warning_msg(const char *warnfmt, ...)
{
    va_list ap;
    /*    fprintf(stderr, "WARNING: "); */
    va_start(ap, warnfmt);
    /*    vfprintf(stderr, warnfmt, ap); */
    PrintLogErr("\nWARNING: ", warnfmt, ap);
    va_end(ap);
    /*    fprintf(stderr, "\n"); */
    return;
}

